package cn.xyz.commons.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.beanutils.BeanUtils;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;

import com.mongodb.DBObject;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;

import cn.xyz.mianshi.utils.SKBeanUtils;

public class DocumentToJavaBean {
	
	/** 把DBObject转换成bean对象 
	 * @param dbObject
	 * @param bean
	 * @return
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	public static <T> T dbObject2Bean(DBObject dbObject, T bean) throws IllegalAccessException, InvocationTargetException {
		if (null == bean)
			return null;
		Field[] fields = bean.getClass().getDeclaredFields();
		for (Field field : fields) {
			String varName = field.getName();
			Object obj = dbObject.get(varName);
			if (null != obj) {
				BeanUtils.setProperty(bean, varName, obj);
			}
		}
		return bean;
	}
	
	/** morphia 下  DBobject to bean
	 * @param morphia
	 * @param dbName
	 * @param t
	 * @param dbObject
	 * @return
	 */
	public static <T> T dbObject2BeanByMorphia(Morphia morphia,String dbName,final Class<T> t,DBObject dbObject){
//		Friends dbObject2Bean = morphia.fromDBObject(morphia.createDatastore(SKBeanUtils.getLocalSpringBeanManager().getMongoClient(), SKBeanUtils.getFriendsManager().getCollectionName(userId)), Friends.class, dbObject);
		Datastore datastore = morphia.createDatastore(SKBeanUtils.getLocalSpringBeanManager().getMongoClient(), dbName);
		T bean = morphia.fromDBObject(datastore, t, dbObject);
		return bean;
	}
	
	
	/**
	* 根据查询的记录转成javabean
	* @param collection
	* @param filter
	* @param obj
	* @return
	*/
	public List queryToBeanList(MongoCollection<Document> collection, Bson filter, Object obj) {
		List list = new ArrayList();
		MongoCursor<Document> cursor = null;
		if (filter != null) {
			cursor = collection.find(filter).iterator();
		} else {
			cursor = collection.find().iterator();
		}
		try {
			while (cursor.hasNext()) {
				Document doc = cursor.next();
				Object object;
				try {
					object = DocumentToJavaBean.setFieldValue(obj.getClass().newInstance(), doc);
					if(null!=object)
						list.add(object);
				} catch (Exception e) {
					e.printStackTrace();
				} 
			}
		} finally {
			cursor.close();
		}
		
		return list;
	}


	/**
	* 根据mongodb中的document记录设置对应的实体类的属性
	* @param obj
	* @param doc
	* @return
	* @throws NoSuchMethodException
	* @throws SecurityException
	* @throws IllegalAccessException
	* @throws IllegalArgumentException
	* @throws InvocationTargetException
	* @throws InstantiationException
	*/
	@SuppressWarnings("rawtypes")
	public static <T> T setFieldValue(T obj, Document doc)
	 {
		try {
			Class clazz = obj.getClass();
			Field[] fields = clazz.getDeclaredFields();
			Method[] methods = clazz.getDeclaredMethods();
			for (Field field : fields) {
				String name = field.getName();
				String setName = parseSetName(name);
				if (!checkSetMethod(methods, setName)) {
					continue;
				}
				String fieldType = field.getType().getName();
				Method fieldSetMet = clazz.getDeclaredMethod(setName, field.getType());
				if ("java.lang.String".equalsIgnoreCase(fieldType)) {
					String value = doc.getString(name);
					if (value == null || "".equals(value)) {
						continue;
					}
					fieldSetMet.invoke(obj, value);
				} else if ("java.lang.Integer".equalsIgnoreCase(fieldType) || "int".equalsIgnoreCase(fieldType)) {
					Integer value = doc.getInteger(name);
					if (value == null) {
						continue;
					}
					fieldSetMet.invoke(obj, value);
				} else if ("java.lang.Long".equalsIgnoreCase(fieldType) || "long".equalsIgnoreCase(fieldType)) {

					Long value = doc.getLong(name);
					if (value == null) {
						continue;
					}
					fieldSetMet.invoke(obj, value);
				} else if ("java.lang.Double".equalsIgnoreCase(fieldType) || "long".equalsIgnoreCase(fieldType)) {
					Double value = doc.getDouble(name);
					if (value == null) {
						continue;
					}
					fieldSetMet.invoke(obj, value);
				} else if ("java.lang.Boolean".equalsIgnoreCase(fieldType) || "boolean".equalsIgnoreCase(fieldType)) {
					Boolean value = doc.getBoolean(name);
					if (value == null) {
						continue;
					}
					fieldSetMet.invoke(obj, value);
				} else if ("java.sql.Timestamp".equalsIgnoreCase(fieldType)) {
					Date date = doc.getDate(name);
					if (date == null) {
						continue;
					}
					fieldSetMet.invoke(obj, new java.sql.Timestamp(date.getTime()));
				} else if ("java.sql.Date".equalsIgnoreCase(fieldType)) {
					Date date = doc.getDate(name);
					if (date == null) {
						continue;
					}
					fieldSetMet.invoke(obj, new java.sql.Date(date.getTime()));
				} else if ("java.util.Date".equalsIgnoreCase(fieldType)) {
					Date date = doc.getDate(name);
					if (date == null) {
						continue;
					}
					fieldSetMet.invoke(obj, date);
				} else if ("java.util.List".equalsIgnoreCase(fieldType)) {
					List<Document> docs = (List<Document>) doc.get(name);
					if (docs == null || docs.size() == 0) {
						continue;
					}
					Class fieldClazz = field.getType(); // 得到field的class及类型全路径+
					Class genericClazz = null;
					if (fieldClazz.isAssignableFrom(List.class)) //
					{
						Type fc = field.getGenericType(); // 关键的地方，如果是List类型，得到其Generic的类型

						if (fc instanceof ParameterizedType) // 如果是泛型参数的类型
						{
							ParameterizedType pt = (ParameterizedType) fc;

							genericClazz = (Class) pt.getActualTypeArguments()[0]; // 得到泛型里的class类型对象。

						}
					}
					List list = new ArrayList<>();
					for (Document document : docs) {
						Object genericobj = genericClazz.newInstance();
						Object object = setFieldValue(genericobj, document);
						if(null!=object)
							list.add(object);
					}
					fieldSetMet.invoke(obj, list);
				} else {
					String docName = field.getType().getSimpleName();
					Document document = (Document) doc.get(docName);
					if (document == null) {
						continue;
					}
					Class fieldClazz = field.getType();
					Object object = fieldClazz.newInstance();
					object = setFieldValue(object, document);
					fieldSetMet.invoke(obj, object);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return obj;
	}



	/**
	* 检查实体类中是否有set方法
	* @param methods
	* @param methodName
	* @return
	*/
	public static boolean checkSetMethod(Method[] methods, String methodName) {
		for (Method method : methods) {
			if (methodName.equals(method.getName())) {
				return true;
			}
		}
		return false;
	}

	/**
	* 根据属性名字拼接set方法名
	* @param name
	* @return
	*/
	public static String parseSetName(String name) {
		if (name == null || "".equals(name)) {
			return null;
		}
		return "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
	}
}
